﻿using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using GameLibrary;
using Rescue_of_the_Dwarvenzombies.Rendering.RenderStates;

namespace Rescue_of_the_Dwarvenzombies.Rendering
{
    /// <summary>
    /// The render manager is repsonsible for drawing all layers and associated objects to the screen.
    /// The behavior of the manager is represented by it's different render states.
    /// </summary>
    public class RenderManager : DrawableGameComponent, IRenderManager
    {
        //Maybe unneccessary?
        private SpriteBatch SpriteBatch { get; set; }

        /// <summary>
        /// Gets the texture dictionary. All textures are stored with the name (string) as the key
        /// </summary>
        public Dictionary<string, Texture2D> TextureDictionary { get; private set; }

        /// <summary>
        /// Gets the pixel data dictionary, used in the collision detection.
        /// It contains several other dictionaries with pixel data for the different sprites.
        /// Access to the inner dictionary is done by passing a source rectangle as key.
        /// </summary>
        public Dictionary<string, Dictionary<Rectangle, Color[]>> PixelDataDictionary { get; private set; }

        /// <summary>
        /// Gets or sets the state of Render manager.
        /// </summary>
        public RenderState CurrentState { get; set; }

        /// <summary>
        /// Gets the render state manager.
        /// </summary>
        public RenderStateManager RenderStateManager { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="RenderManager"/> class.
        /// </summary>
        /// <param name="game">The Game that the game component should be attached to.</param>
        public RenderManager(Game game)
            : base(game)
        {
            game.Services.AddService(typeof(IRenderManager), this);

            Initialize();
            LoadContent();
            InitPixelData();

            RenderStateManager = new RenderStateManager(game);
        }

        /// <summary>
        /// Called when graphics resources need to be loaded.
        /// </summary>
        protected override sealed void LoadContent()
        {
            TextureDictionary = new Dictionary<string, Texture2D>();
            TextureDictionary.Add(Constant.DWARF_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.DWARF_TEXTURE_NAME));
            TextureDictionary.Add(Constant.AXE_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.AXE_TEXTURE_NAME));
            TextureDictionary.Add(Constant.ZOMBIE_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.ZOMBIE_TEXTURE_NAME));
            TextureDictionary.Add(Constant.GROUND_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.GROUND_TEXTURE_NAME));
            TextureDictionary.Add(Constant.TIMEBAR_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.TIMEBAR_TEXTURE_NAME));
            TextureDictionary.Add(Constant.POTION_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.POTION_TEXTURE_NAME));
            TextureDictionary.Add(Constant.WALL_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.WALL_TEXTURE_NAME));
            TextureDictionary.Add(Constant.GROUND_SPAWN_TEXTURE_NAME, Game.Content.Load<Texture2D>(Constant.GROUND_SPAWN_TEXTURE_NAME));

            SpriteBatch = new SpriteBatch(GraphicsDevice);
            base.LoadContent();
        }

        /// <summary>
        /// Called when the GameComponent needs to be updated.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Update</param>
        public override void Update(GameTime gameTime)
        {
            CurrentState.Update(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// Called when the DrawableGameComponent needs to be drawn.
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to Draw.</param>
        public override void Draw(GameTime gameTime)
        {
            CurrentState.Draw();

            base.Draw(gameTime);
        }

        /// <summary>
        /// Initializes the pixel data for all animations in the sprite sheets
        /// (used in the pixel collision detection)
        /// </summary>
        private void InitPixelData()
        {
            Texture2D texture;
            Color[] texturePixelData;

            PixelDataDictionary = new Dictionary<string, Dictionary<Rectangle, Color[]>>();

            #region Dwarf pixel data

            Dictionary<Rectangle, Color[]> dwarfPixelData = new Dictionary<Rectangle, Color[]>();

            //Get the texture and store it's data in a color array
            texture = TextureDictionary[Constant.DWARF_TEXTURE_NAME];
            texturePixelData = new Color[texture.Width * texture.Height];
            texture.GetData(texturePixelData);

            //Loop through all animations in the sprite sheet
            for (int y = 0; y < Constant.DWARF_ANIMATIONS_Y; y++)
            {
                for (int x = 0; x < Constant.DWARF_ANIMATIONS_X; x++)
                {
                    int startX = 0;
                    int startY = 0;
                    //Calculate the coordinates of the animation (including offset)
                    startX += Constant.DWARF_RECTANGLE_WIDTH * x;
                    if (x > 0)
                        startX += Constant.DWARF_RECTANGLE_OFFSET_X * x;

                    startY += Constant.DWARF_RECTANGLE_HEIGHT * y;
                    if (y > 0)
                        startY += Constant.DWARF_RECTANGLE_OFFSET_Y * y;

                    //Create the animation source rectangle
                    Rectangle sourceRectangle = new Rectangle(startX, startY, Constant.DWARF_RECTANGLE_WIDTH, Constant.DWARF_RECTANGLE_HEIGHT);

                    // Retrieve the pixel data for the specific animation
                    Color[] pixelData = GetAnimationPixelData(texturePixelData, sourceRectangle, texture.Width);

                    //Save the pixel data with the source rectangle as the key
                    dwarfPixelData.Add(sourceRectangle, pixelData);
                }
            }

            PixelDataDictionary.Add(Constant.DWARF_TEXTURE_NAME, dwarfPixelData);

            #endregion Dwarf pixel data

            #region Zombie pixel data

            Dictionary<Rectangle, Color[]> zombiePixelData = new Dictionary<Rectangle, Color[]>();

            //Get the texture and store it's data in a color array
            texture = TextureDictionary[Constant.ZOMBIE_TEXTURE_NAME];
            texturePixelData = new Color[texture.Width * texture.Height];
            texture.GetData(texturePixelData);

            //Loop through all animations in the sprite sheet
            for (int y = 0; y < Constant.ZOMBIE_ANIMATIONS_Y; y++)
            {
                for (int x = 0; x < Constant.ZOMBIE_ANIMATIONS_X; x++)
                {
                    int startX = 0;
                    int startY = 0;
                    //Calculate the coordinates of the animation (including offset)
                    startX += Constant.ZOMBIE_RECTANGLE_WIDTH * x;
                    if (x > 0)
                        startX += Constant.ZOMBIE_RECTANGLE_OFFSET_X * x;

                    startY += Constant.ZOMBIE_RECTANGLE_HEIGHT * y;
                    if (y > 0)
                        startY += Constant.ZOMBIE_RECTANGLE_OFFSET_Y * y;

                    //Create the animation source rectangle
                    Rectangle sourceRectangle = new Rectangle(startX, startY, Constant.ZOMBIE_RECTANGLE_WIDTH, Constant.ZOMBIE_RECTANGLE_HEIGHT);

                    // Retrieve the pixel data for the specific animation
                    Color[] pixelData = GetAnimationPixelData(texturePixelData, sourceRectangle, texture.Width);

                    //Save the pixel data with the source rectangle as the key
                    zombiePixelData.Add(sourceRectangle, pixelData);
                }
            }

            PixelDataDictionary.Add(Constant.ZOMBIE_TEXTURE_NAME, zombiePixelData);

            #endregion Zombie pixel data

            #region Weapon pixel data

            Dictionary<Rectangle, Color[]> weaponPixelData = new Dictionary<Rectangle, Color[]>();

            //Get the texture and store it's data in a color array
            texture = TextureDictionary[Constant.AXE_TEXTURE_NAME];
            texturePixelData = new Color[texture.Width * texture.Height];
            texture.GetData(texturePixelData);

            //Loop through all animations in the sprite sheet
            for (int y = 0; y < Constant.AXE_ANIMATIONS_Y; y++)
            {
                for (int x = 0; x < Constant.AXE_ANIMATIONS_X; x++)
                {
                    int startX = 0;
                    int startY = 0;
                    //Calculate the coordinates of the animation (including offset)
                    startX += Constant.AXE_RECTANGLE_WIDTH * x;
                    if (x > 0)
                        startX += Constant.AXE_RECTANGLE_OFFSET_X * x;

                    startY += Constant.AXE_RECTANGLE_HEIGHT * y;
                    if (y > 0)
                        startY += Constant.AXE_RECTANGLE_OFFSET_Y * y;

                    //Create the animation source rectangle
                    Rectangle sourceRectangle = new Rectangle(startX, startY, Constant.AXE_RECTANGLE_WIDTH, Constant.AXE_RECTANGLE_HEIGHT);

                    //Retrieve the pixel data for the specific animation
                    Color[] pixelData = GetAnimationPixelData(texturePixelData, sourceRectangle, texture.Width);

                    //Save the pixel data with the source rectangle as the key
                    weaponPixelData.Add(sourceRectangle, pixelData);
                }
            }

            PixelDataDictionary.Add(Constant.AXE_TEXTURE_NAME, weaponPixelData);

            #endregion Weapon pixel data

            #region Item pixel data //Use this for objects without animation by still needs pixel collision

            //Dictionary<Rectangle, Color[]> itemPixelData = new Dictionary<Rectangle, Color[]>();

            ////Get the texture and store it's data in a color array
            //texture = TextureDictionary[Constant.POTION_TEXTURE_NAME];
            //texturePixelData = new Color[texture.Width * texture.Height];
            //texture.GetData(texturePixelData);

            ////Create the animation source rectangle
            //Rectangle rectangle = new Rectangle(0, 0, Constant.POTION_RECTANGLE_WIDTH, Constant.POTION_RECTANGLE_HEIGHT);

            ////Save the pixel data with the source rectangle as the key
            //itemPixelData.Add(rectangle, texturePixelData);

            //PixelDataDictionary.Add(Constant.POTION_TEXTURE_NAME, itemPixelData);

            #endregion Item pixel data
        }

        /// <summary>
        /// Collects the pixel data for a specific animation in a sprite sheet
        /// </summary>
        /// <param name="texturePixelData">The pixel data for the sprite sheet</param>
        /// <param name="sourceRectangle">the current source rectangle</param>
        /// <param name="textureWidth">the width of the sprite sheet</param>
        /// <returns>The pixel data for a specific animation</returns>
        private Color[] GetAnimationPixelData(Color[] texturePixelData, Rectangle sourceRectangle, int textureWidth)
        {
            Color[] pixelData = new Color[(sourceRectangle.Right - sourceRectangle.Left) * (sourceRectangle.Bottom - sourceRectangle.Top)];

            int i = 0;
            //Loop through the animation, row by row
            for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++)
            {
                for (int x = sourceRectangle.Left; x < sourceRectangle.Right; x++)
                {
                    //Get the corresponding pixel data from the sprite sheet
                    pixelData[i++] = texturePixelData[x + (y * textureWidth)];
                }
            }
            return pixelData;
        }
    }
}